import wx
import wx.grid as Grid
import db
import setting
from sqlalchemy.orm import joinedload

class DBTable(Grid.PyGridTableBase):
	"""
	A custom wx.Grid Table using user supplied data
	"""
	def __init__(self, table, plugins):
		"""data is a list of the form
		[(rowname, dictionary),
		dictionary.get(colname, None) returns the data for column
		colname
		"""
		# The base class must be initialized *first*
		Grid.PyGridTableBase.__init__(self)
		self.table = table
		self.colnames = self.get_colnames()
		self.session = setting.Session()
		self.data = self.fetch_data()
		self.plugins = plugins or {}
		# XXX
		# we need to store the row length and column length to
		# see if the table has changed size
		self._rows = self.GetNumberRows()
		self._cols = self.GetNumberCols()

	def get_colnames(self):
		cols = self.table.__table__.columns.keys()[1:]
		self.fkeys = []
		length = len(cols)
		for i in xrange(length):
			if cols[i].endswith("_id"):
				cols[i] = cols[i][:-3]
				self.fkeys.append(cols[i])
		return cols

	def fetch_data(self):
		opts = []
		for k in self.fkeys:
			opts.append(joinedload(k))
		return self.session.query(self.table).options(*opts).all()

	def GetNumberCols(self):
		return len(self.colnames)

	def GetNumberRows(self):
		return len(self.data)

	def GetColLabelValue(self, col):
		r = self.colnames[col]
		return _(r)

	#def GetRowLabelValue(self, row):
	#	return getattr(self.data[row], "id")

	def GetValue(self, row, col):
		return unicode(getattr(self.data[row], self.colnames[col]))

	def GetRawValue(self, row, col):
		return getattr(self.data[row], self.colnames[col])

	def SetValue(self, row, col, value):
		setattr(self.data[row], self.colnames[col], value)

	def ResetView(self, grid):
		"""
		(Grid) -> Reset the grid view.   Call this to
		update the grid if rows and columns have been added or deleted
		"""
		self.data = self.fetch_data()
		grid.BeginBatch()

		for current, new, delmsg, addmsg in [
			(self._rows, self.GetNumberRows(), Grid.GRIDTABLE_NOTIFY_ROWS_DELETED, Grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
			(self._cols, self.GetNumberCols(), Grid.GRIDTABLE_NOTIFY_COLS_DELETED, Grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
		]:

			if new < current:
				msg = Grid.GridTableMessage(self,delmsg,new,current-new)
				grid.ProcessTableMessage(msg)
			elif new > current:
				msg = Grid.GridTableMessage(self,addmsg,new-current)
				grid.ProcessTableMessage(msg)
				self.UpdateValues(grid)

		grid.EndBatch()

		self._rows = self.GetNumberRows()
		self._cols = self.GetNumberCols()
		# update the column rendering plugins
		self._updateColAttrs(grid)

		# update the scrollbars and the displayed part of the grid
		grid.AdjustScrollbars()
		grid.ForceRefresh()


	def UpdateValues(self, grid):
		"""Update all displayed values"""
		# This sends an event to the grid table to update all of the values
		msg = Grid.GridTableMessage(self, Grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
		grid.ProcessTableMessage(msg)

	def _updateColAttrs(self, grid):
		"""
		wx.Grid -> update the column attributes to add the
		appropriate renderer given the column name.  (renderers
		are stored in the self.plugins dictionary)

		Otherwise default to the default renderer.
		"""
		col = 0

		for colname in self.colnames:
			attr = Grid.GridCellAttr()
			if colname in self.plugins:
				renderer = self.plugins[colname](self)

				if renderer.colSize:
					grid.SetColSize(col, renderer.colSize)

				if renderer.rowSize:
					grid.SetDefaultRowSize(renderer.rowSize)

				attr.SetReadOnly(True)
				attr.SetRenderer(renderer)

			grid.SetColAttr(col, attr)
			col += 1

	# ------------------------------------------------------
	# begin the added code to manipulate the table (non wx related)
	def AppendRow(self, row):
		#print 'append'
		entry = {}

		for name in self.colnames:
			entry[name] = "Appended_%i"%row

		# XXX Hack
		# entry["A"] can only be between 1..4
		entry["A"] = random.choice(range(4))
		self.data.insert(row, ["Append_%i"%row, entry])

	def DeleteCols(self, cols):
		"""
		cols -> delete the columns from the dataset
		cols hold the column indices
		"""
		# we'll cheat here and just remove the name from the
		# list of column names.  The data will remain but
		# it won't be shown
		deleteCount = 0
		cols = cols[:]
		cols.sort()

		for i in cols:
			self.colnames.pop(i-deleteCount)
			# we need to advance the delete count
			# to make sure we delete the right columns
			deleteCount += 1

		if not len(self.colnames):
			self.data = []

	def DeleteRows(self, rows):
		"""
		rows -> delete the rows from the dataset
		rows hold the row indices
		"""
		deleteCount = 0
		rows = rows[:]
		rows.sort()

		for i in rows:
			self.data.pop(i-deleteCount)
			# we need to advance the delete count
			# to make sure we delete the right rows
			deleteCount += 1

	def SortColumn(self, col):
		"""
		col -> sort the data based on the column indexed by col
		"""
		name = self.colnames[col]
		_data = []

		for row in self.data:
			rowname, entry = row
			_data.append((entry.get(name, None), row))

		_data.sort()
		self.data = []

		for sortvalue, row in _data:
			self.data.append(row)

# end table manipulation code
# ----------------------------------------------------------
class DBGrid(Grid.Grid):
	def __init__(self, parent, table):
		Grid.Grid.__init__(self, parent, -1, size=(100, 100))
		#try:
		self._table = DBTable(table, None)
		self.SetTable(self._table)
		#except:
		#	print "table %s not find" % table
		#	self.CreateGrid(50, 17)

	def Reset(self):
		self._table.ResetView(self)

